package org.aicfve.fastjsonrpc;

import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import org.aicfve.fastjsonrpc.entity.ErrorObject;
import org.aicfve.fastjsonrpc.entity.RequestObject;
import org.aicfve.fastjsonrpc.entity.ResponseObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

public class FastJsonRpcHandler {
    private static final Logger logger = LoggerFactory.getLogger(FastJsonRpcHandler.class);

    private static HashMap<String,RpcMethod> rpc_methods;

    public FastJsonRpcHandler(){
        rpc_methods = new HashMap<>();
    }

    public void addService(Object bean){
        logger.info("method addService [bean]:{}",bean);

        Class clazz = bean.getClass();
        logger.debug("method addService [clazz]:{}",clazz);

        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods)
        {
            logger.debug("[method]:{}",method);

            String method_name = method.getName();
            logger.info("method addService [method.name]:{}",method_name);
            Class[] param_types =  method.getParameterTypes();
            for (Class param_clazz : param_types)
            {
                logger.debug("method addService [param_type]:{}",param_clazz);
            }

            Class return_type = method.getReturnType();

            RpcMethod rpcMethod = new RpcMethod(bean,clazz.getSimpleName(),method,param_types,return_type);

            rpc_methods.put(method_name,rpcMethod);
        }
    }

    public void handler(RequestObject request, ResponseObject response)
    {
        logger.debug("method handler [request]:{} [response]:{}", request, response);
        response.jsonrpc = request.jsonrpc;
        response.id = request.id;

        try {
            response.result = this.invoke(request.method,request.params);
        } catch (InvocationTargetException e) {
            logger.error("method handler invoke exception:{}",e.getTargetException().getMessage());
            response.error = new ErrorObject(-32600,e.getTargetException().getMessage());
        } catch (IllegalAccessException e) {
            logger.error("method handler invoke exception:{}",e.getMessage());
            response.error = new ErrorObject(-32000,e.getMessage());
        } catch (InstantiationException e) {
            logger.error("method handler invoke exception:{}",e.getMessage());
            response.error = new ErrorObject(-32000,e.getMessage());
        }

        logger.debug("method handler [response]:{}", response);
    }

    public Object invoke(String method,Object ... json_params) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        logger.debug("method invoke [method]:{} [json_params]:{}",method,json_params);
        RpcMethod rpc_method = rpc_methods.get(method);
        if(rpc_method == null)
            throw new InvocationTargetException(new Exception("rpc method is null"));

        Class [] param_types = rpc_method.param_types;

        Object [] params = new Object[param_types.length];

        if(json_params != null)
            if(json_params.length == param_types.length)
                for (int index = 0; index < param_types.length; index++) {
                    params[index] = TypeUtils.cast(json_params[index], param_types[index], ParserConfig.getGlobalInstance());
                }
            else
            {
                throw new InvocationTargetException(new Exception("param_types.length != param_jsons.length"));
            }

        return rpc_method.method.invoke(rpc_method.bean,params);
    }
}
